/*
 *
 * 文件信息模块
 *
 * 存储和组织文件的所有信息，
 * 提供对文件信息的序列化和反序列化接口，支持json和tuple格式
 *
 **/
#pragma once

#include <string>
#include <tuple>
#include "util.hh"
#include "config.hh"

namespace cloud
{
namespace util
{
// filepath <-> backpath <-> packpath <-> downloadurl
struct path : easyfs::path
{
    path(const std::string& s) : easyfs::path(s) {}

    static std::string fpath2bpath(const std::string& s)
    {
        return (easyfs::path(config::instance().back_dir()) / s).string();
    }
    static std::string fpath2ppath(const std::string& s)
    {
        return (easyfs::path(config::instance().pack_dir()) / s + config::instance().pack_suffix()).string();
    }
    static std::string fpath2dlurl(const std::string& s)
    {
        return (easyfs::path(config::instance().download_prefix()) / s).string();
    }

    static std::string bpath2fpath(const std::string& s)
    {
        return easyfs::path(s).remove_top_directory().string();
    }
    static std::string bpath2ppath(const std::string& s)
    {
        return easyfs::path(s).replace_top_directory(config::instance().pack_dir()).string() + config::instance().pack_suffix();
    }
    static std::string bpath2dlurl(const std::string& s)
    {
        return easyfs::path(s).replace_top_directory(config::instance().download_prefix()).string();
    }

    static std::string ppath2fpath(const std::string& s)
    {
        return easyfs::path(s).remove_top_directory().stem().string();
    }
    static std::string ppath2bpath(const std::string& s)
    {
        return fpath2bpath(ppath2fpath(s));
    }
    static std::string ppath2dlurl(const std::string& s)
    {
        return fpath2dlurl(ppath2fpath(s));
    }

    static std::string dlurl2fpath(const std::string& s)
    {
        return easyfs::path(s).remove_top_directory().remove_top_directory().string();
    }
    static std::string dlurl2bpath(const std::string& s)
    {
        return fpath2bpath(dlurl2fpath(s));
    }
    static std::string dlurl2ppath(const std::string& s)
    {
        return fpath2ppath(dlurl2fpath(s));
    }

    static bool exists(const std::string& s)
    {
        return easyfs::exists(s);
    }
    static bool isfile(const std::string& s)
    {
        return easyfs::is_file(s);
    }
    static bool isdir(const std::string& s)
    {
        return easyfs::is_directory(s);
    }
};
}

struct backup_info
{
    // Tuple types that can be converted to backup_info.
    using tuple_of_info = std::tuple<bool, \
                                     size_t, time_t, time_t, time_t, \
                                     std::string, std::string, std::string, std::string>;

    bool        ishot; // is hot
    size_t      fsize; // file size
    time_t      atime; // access time
    time_t      mtime; // modify time
    time_t      ctime; // change time
    std::string fpath; // file path
    std::string bpath; // back path
    std::string ppath; // pack path
    std::string dlurl; // download url

    backup_info()
        : ishot(false)
        , fsize(0)
        , atime(0)
        , mtime(0)
        , ctime(0)
    {}

    backup_info(const std::string& file_path)
    {
        ishot = true;

        fpath = file_path;
        bpath = util::path::fpath2bpath(fpath);
        ppath = util::path::fpath2ppath(fpath);
        dlurl = util::path::fpath2dlurl(fpath);

        util::file file(bpath);
        fsize = file.size();
        atime = file.atime();
        mtime = file.mtime();
        ctime = file.ctime();
    }

    // Initialize members though the json container.
    backup_info(const util::json& js) { parserfrom(js); }

    // Initialize members though the tuple container.
    backup_info(const tuple_of_info& tp) { parserfrom(tp); }

    // Convert to json container.
    util::json parser_to_json() const
    {
        util::json js;
        js.set("ishot", ishot);
        js.set("fsize", fsize);
        js.set("atime", atime);
        js.set("mtime", mtime);
        js.set("ctime", ctime);
        js.set("fpath", fpath);
        js.set("bpath", bpath);
        js.set("ppath", ppath);
        js.set("dlurl", dlurl);
        return js;
    }

    // Convert to tuple container.
    tuple_of_info parser_to_tuple() const
    {
        return tuple_of_info(ishot, fsize, atime, mtime, ctime, fpath, bpath, ppath, dlurl);
    }

    // Convert from json container.
    void parserfrom(const util::json& js)
    {
        ishot = js.get<bool>("ishot");
        fsize = js.get<size_t>("fsize");
        atime = js.get<time_t>("atime");
        mtime = js.get<time_t>("mtime");
        ctime = js.get<time_t>("ctime");
        fpath = js.get<std::string>("fpath");
        bpath = js.get<std::string>("bpath");
        ppath = js.get<std::string>("ppath");
        dlurl = js.get<std::string>("dlurl");
    }

    // Convert from tuple container.
    void parserfrom(const tuple_of_info& tp)
    {
        ishot = std::get<0>(tp);
        fsize = std::get<1>(tp);
        atime = std::get<2>(tp);
        mtime = std::get<3>(tp);
        ctime = std::get<4>(tp);
        fpath = std::get<5>(tp);
        bpath = std::get<6>(tp);
        ppath = std::get<7>(tp);
        dlurl = std::get<8>(tp);
    }

    std::string etag()
    {
        return fpath + '_' + std::to_string(fsize); //  + '_' + std::to_string(mtime);
                                                    //  不可带 modify time
    }

    std::string content()
    {
        if (ishot)
        {
            return util::file(bpath).read();
        }
        else
        {
            util::file(ppath).unpack(bpath).remove();
            ishot = true;
            return content();
        }
    }
};

}
